﻿using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System;
using System.IO;
using System.Text;

/*
* Unity 导入Texture的处理操作
* 对于Texture的处理主要是针对使用TexturePacker打包引入的图集的处理以及常用的Texture的纹理处理
*/

public class ProcessTexture : AssetPostprocessor
{
    static DateTime excute_command_time = new DateTime();
    static bool is_add_update_func = false;
    static readonly float PROCESS_TEXTUER_DELAY_TIME = 2f;

    static TextureMD5Manager tex_md5 = TextureMD5Manager.Instance;
    static string project_root_path { get { return Application.dataPath.Replace("Assets", ""); } }
    
    // 是否打开自动设置开关
    public readonly static bool IS_OPEN_AUTO_CONVERT_FORMAT = true;

    #region UnityEditor拓展
    //[MenuItem("FormatTexture/Test",false,101)]
    //public static void TTTT()
    //{
    //    tex_md5.AddFileMD5("21312@@@@3123", "dfdf@@@@sdfdsf");
    //    tex_md5.AddFileMD5("sdfsdssddf", "fwefwef");
    //    tex_md5.AddFileMD5("213123123", "dfdfsdfdsf");
    //    tex_md5.SaveLog();
    //}



    [MenuItem("Assets/FormatTexture/ProcessSelectionFormat__Atlas", false, 102)]
    [MenuItem("FormatTexture/ProcessSelectionFormat__Atlas", false, 102)]
    public static void ProcessSelectionAtlasFormat()
    {
        var selections = UnityEditor.Selection.objects;
        foreach (var obj in selections)
        {
            var asset_path = AssetDatabase.GetAssetPath(obj);
            if (CheckIsAtleasFile(asset_path))
            {
                var importer = GetImporter(asset_path);
                ConverTextureFormatToAtlerFormat(asset_path, importer);
                SetUIAtlas(asset_path);

                SaveFileMD5(asset_path);
            }
        }
    }

    [MenuItem("Assets/FormatTexture/ProcessSelectionFormat__Txture", false, 103)]
    [MenuItem("FormatTexture/ProcessSelectionFormat__Txture", false, 103)]
    public static void ProcessSelectionTxtureFormat()
    {
        var selections = UnityEditor.Selection.objects;
        foreach (var obj in selections)
        {
            var asset_path = AssetDatabase.GetAssetPath(obj);
            var _path = asset_path.ToLower();
            if (_path.EndsWith(".jpg") || _path.EndsWith(".png"))
            {
                var importer = GetImporter(asset_path);
                ConverTextureFormatToAtlerFormat(asset_path, importer);

                SaveFileMD5(asset_path);
            }
        }
    }

    #endregion

    /// <summary>
    /// 导入之后的后缀处理
    /// 方法内容是对TexturePacker打包的图集进行处理
    /// </summary>
    static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
    {
        //foreach(var add in importedAssets)
        //{
        //    Debug.Log("importedAssets  " + add);
        //}

        //foreach (var del in deletedAssets)
        //{
        //    Debug.Log("deletedAssets  " + del);
        //}

        //int count = movedAssets.Length;
        //for(int i = 0; i < count; ++i)
        //{
        //    Debug.Log("movedAssets   from : " + movedFromAssetPaths[i] + "  to :  " + movedAssets[i]);
        //}

        // 处理图片格式
        if(IS_OPEN_AUTO_CONVERT_FORMAT)
        {
            OnProcessTexture(importedAssets, deletedAssets, movedAssets, movedFromAssetPaths);
        }
    }

    #region 执行流程

    private static void OnProcessTexture(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
    {
        /*********************************************************************
         * 
         * Unity中所有改动过的文件都会经过这个函数
         * 在这里可能会遇到几种情况
         * 1.删除了图集
         * 2.在这个函数里面执行了贴图格式设置之后，Unity会再次调用这个函数，这里需要避免死循环和重复导入
         * 
        ***********************************************************************/

        // 排除移动的文件 过滤掉所有非不需要处理的文件
        // 删除掉所有因移动文件而导致的重复导入资源
        // 如果导入资源在移动文件里面就表示是移动的文件,需要排除掉移动文件
        List<string> add_assets = ImportFielterAssets(importedAssets, movedAssets);
        List<string> del_assets = DeleteFielterAssets(deletedAssets,movedFromAssetPaths);
        Dictionary<string,string> moved_assets = MovedFielterAssets(movedAssets, movedFromAssetPaths);

        // 排除掉重复资源之后就是查看是否能够被导入
        // 如果是导入资源，就查看这个MD5值存不存在或者md5值相不相等  如果真就处理 并备案处理
        // 如果是移动资源，那么就查看这个资源的旧MD5是否记录在案，如果记录在案则比较MD5值，如果都相等就改变文件路径
        // 如果删除资源，就查找这个文件是否在备案中，如果在备案中，就删除
        // 存储
        TextureFormatAssetContainer.ImportAssets = add_assets;
        TextureFormatAssetContainer.DelectAssets = del_assets;
        TextureFormatAssetContainer.MovedAssets = moved_assets;

        InitProcessFunc();
    }

    /// <summary>
    /// 导入过滤文件
    /// </summary>
    /// <param name="importedAssets">导入文件的路径</param>
    /// <param name="movedAssets">移动的文件路径</param>
    /// <returns>所有导入的，非移动的被处理资源路径</returns>
    private static List<string> ImportFielterAssets(string[] importedAssets, string[] movedAssets)
    {
        // 从添加文件中排除移动的文件 过滤掉所有非不需要处理的文件
        // 删除掉所有因移动文件而导致的重复导入资源
        // 如果导入资源在移动文件里面就表示是移动的文件,需要排除掉移动文件
        List<string> list = new List<string>();
        if (importedAssets == null || importedAssets.Length <= 0) return list;

        List<string> r_list = new List<string>();
        if(movedAssets != null && movedAssets.Length > 0) r_list.AddRange(movedAssets);

        foreach (var asset in importedAssets)
        {
            var is_format_asset = CheckIsAtleasFile(asset) || CheckIsTextureFile(asset);

            if (is_format_asset)
            {
                // 如果是移动资源则排除，以防重复添加
                if (r_list.Count > 0 || !r_list.Contains(asset))
                {
                    list.Add(asset);
                }
                else
                {
                    list.Add(asset);
                }
            }
        }
        return list;
    }

    /// <summary>
    /// 移动文件过滤
    /// </summary>
    /// <param name="moved_to_paths">移动到目标路径</param>
    /// <param name="moved_from_path">移动原路径</param>
    /// <returns>返回字典<from,to>形式</returns>
    private static Dictionary<string, string> MovedFielterAssets(string[] moved_to_paths,string[] moved_from_path)
    {
        Dictionary<string, string> moved_assets_dict = new Dictionary<string, string>();
        if(moved_to_paths == null || moved_to_paths.Length <=0) return moved_assets_dict;

        int count = moved_to_paths.Length;
        for(int i = 0; i < count; ++i)
        {
            var to = moved_to_paths[i];
            var is_format_asset = (CheckIsAtleasFile(to) || CheckIsTextureFile(to));
            if(is_format_asset)
            {
                moved_assets_dict.Add(moved_from_path[i], to);
            }
        }

        return moved_assets_dict;
    }

    /// <summary>
    /// 移除资源过滤
    /// </summary>
    /// <param name="deletedAssets">移除掉的资源</param>
    /// <param name="movedFromAssetPaths">被移动走的文件</param>
    /// <returns>被移除掉的，需要被处理记录在案的资源路径</returns>
    private static List<string> DeleteFielterAssets(string[] deletedAssets, string[] movedFromAssetPaths)
    {
        // 排除掉所有移动的需要处理的文件
        // 先排除掉所有删除文件中不需要排除的文件
        // 再排除掉所有文件移动操作而导致删除的文件
        // 没有移除资源就直接返回
        var list = new List<string>();
        if(deletedAssets == null || deletedAssets.Length <= 0) return list;

        var move_list = new List<string>();
        if (movedFromAssetPaths != null && movedFromAssetPaths.Length > 0) move_list.AddRange(movedFromAssetPaths);

        foreach (var del in deletedAssets)
        {
            if (CheckIsAtleasFile(del) || CheckIsTextureFile(del))
            {
                if (move_list.Count > 0 && !move_list.Contains(del))
                {
                    list.Add(del);
                }
                else
                {
                    list.Add(del);
                }
            }
        }

        return list;
    }

    static void InitProcessFunc()
    {
        // 制造一个延迟函数
        // Unity自身的处理资源有可能被执行2次，为了避免多次执行和执行补全的情况，使用延迟
        // 真正的逻辑处理在Update中
        excute_command_time = DateTime.Now.AddSeconds(PROCESS_TEXTUER_DELAY_TIME);
        if (!is_add_update_func)
        {
            is_add_update_func = true;
            EditorApplication.update += Update;
        }
    }

    static void Update()
    {
        if (CheckCanProcessTexture())
        {
            // 把Update的方法给删除掉
            EditorApplication.update -= Update;
            BeginProcessTexture();
            FinishProcessTexture();
        }
    }

    static bool CheckCanProcessTexture()
    {
        return DateTime.Now.CompareTo(excute_command_time) >= 0;
    }

    static void BeginProcessTexture()
    {
        // 只有新增的是需要被处理的
        // 除了需要处理贴图格式，额外还需要维护MD5这张表
        // 如果没有了MD5这张表，那么下次就会重新格式化贴图，增加处理时间

        var moved_list = TextureFormatAssetContainer.MovedAssets;
        foreach (var moved in moved_list)
        {
            var from_asset_path = moved.Key;
            if (tex_md5.CheckHasFileMD5(from_asset_path))
            {
                // 在移动文件的事件中可能发生两种事情，一个是只有进行移动文件的操作
                // 另外一个操作时在移动文件完成后，再进行了文件替换操作
                // 所以这里需要进行的处理动作是不仅要检测它是否是移动的文件
                // 还要检测是否移动后被改动过

                var to_asset_path = moved.Value;
                var full_path = PathCombine(project_root_path, to_asset_path);
                var md5 = TextureMD5Manager.GetMD5HashFromFile(full_path);
                if(tex_md5.CheckFileChanged(from_asset_path,md5))
                {
                    Debug.Log("BeginProcessTexture  opt : moved and then changed  full_path: " + PathCombine(project_root_path, from_asset_path));
                    //tex_md5.MoveFileMD5(from_asset_path, to_asset_path);
                    //tex_md5.ChangeFileMD5(to_asset_path, md5);
                    // 如果移动后还早替换操作,则删除MD5指引并且按照新增图片进行处理
                    tex_md5.RemoveFileMD5(from_asset_path);
                    var list = new List<string>();
                    list.Add(to_asset_path);
                    TextureFormatAssetContainer.ImportAssets = list;
                }
                else
                {
                    Debug.Log("BeginProcessTexture  opt : moved  only moved  full_path: " + PathCombine(project_root_path, from_asset_path));
                    tex_md5.MoveFileMD5(from_asset_path, to_asset_path);
                }
            }
        }

        // 匹配MD5，查看是否需要格式化
        // 对新增资源进行格式格式化
        // 对文件生成MD5并保存MD5值
        var add_list = TextureFormatAssetContainer.ImportAssets;

        foreach(var add in add_list)
        {
            var full_path = PathCombine(project_root_path, add);

            var md5 = TextureMD5Manager.GetMD5HashFromFile(full_path);

            // 检查是否存在该MD5，如果没有则执行 又或者 该MD5已经改变过
            if (!tex_md5.CheckHasFileMD5(add) || tex_md5.CheckFileChanged(add, md5))
            {
                //格式化贴图
                // 先进行贴图设置
                // 再判断是否是图集,如果是图集则进行图集处理
                var import = GetImporter(add);
                if(import != null)
                {
                    ConverTextureFormatToAtlerFormat(add,import);
                    if (CheckIsAtleasFile(add)) SetUIAtlas(add);

                    SaveFileMD5(add, md5);
                }
            }
        }

        // 维护MD5
        var del_list = TextureFormatAssetContainer.DelectAssets;
        foreach (var del in del_list)
        {
            if (tex_md5.CheckHasFileMD5(del))
            {
                Debug.Log("BeginProcessTexture  opt : del  full_path: " + PathCombine(project_root_path, del));
                tex_md5.RemoveFileMD5(del);
            }
        }

        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
    }

    /// <summary>
    /// 获取贴图格式
    /// 可以直接接入json表去做自动格式配置
    /// </summary>
    static TextureImporter GetImporter(string image_asset_path)
    {
        //var tex = AssetDatabase.LoadAssetAtPath<Texture>(image_path);
        var path = Path.Combine(project_root_path, image_asset_path);
        if(File.Exists(path))
        {
            return AssetImporter.GetAtPath(image_asset_path) as TextureImporter;
        }

        return null;
    }

    /// <summary>
    /// 保存和维护贴图的MD5，以防再次执行自动格式化
    /// </summary>
    /// <param name="asset_path"></param>
    private static void SaveFileMD5(string asset_path,string md5 = "")
    {
        // 维护MD5
        if (string.IsNullOrEmpty(md5))
        {
            var full_path = PathCombine(project_root_path, asset_path);
            md5 = TextureMD5Manager.GetMD5HashFromFile(full_path);
        }
        //else md5 = md5; 

        if (!tex_md5.CheckHasFileMD5(asset_path))
        {
            Debug.Log(" add  path :  " + asset_path + "|" + md5);
            tex_md5.AddFileMD5(asset_path, md5);
        }
        else
        {
            Debug.Log(" change  path :  " + asset_path + "|" + md5);
            tex_md5.ChangeFileMD5(asset_path, md5);
        }
    }

    // 结束重置状态
    static void FinishProcessTexture()
    {
        excute_command_time = DateTime.Now;
        is_add_update_func = false;
        TextureFormatAssetContainer.Clear();
        tex_md5.SaveLog();
        Debug.Log(" ___FinishProcessTexture___ ");
    }
    #endregion


    #region 转换贴图格式
    // 查看是否是图集路径
    private static bool CheckIsAtleasFile(string asset_path)
    {
        // Atlas 是SN图集路径
        if (asset_path.EndsWith(".png") && (asset_path.Contains("Atlaes") || asset_path.Contains("Atlas")))
        {
            // var full_path = PathCombine(project_root_path, asset_path);
            // 图集一定是png结尾
            return true;
        }
        else
        {
            return false;
        }
    }
    // 查看是否是Texture路径
    private static bool CheckIsTextureFile(string asset_path)
    {
        if ((asset_path.EndsWith(".png") || asset_path.EndsWith(".jpg")) && asset_path.Contains("Textures"))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    // 图集处理
    private static void ConverTextureFormatToAtlerFormat(string img_path,TextureImporter texture_impoter)
    {
        var format = TextureFormatTool.GetTextureFormatFromConfig(img_path);
        var path = img_path.ToLower();
        SetTextureFormat(texture_impoter, format, path.EndsWith(".png"));
    }

    // 格式化图片 
    private static void SetTextureFormat(TextureImporter tex_importer, TextureImporterFormat format , bool alphaIsTransparency)
    {
        // 进行贴图的统一的优化设置
        // 再设置纹理格式
#if UNITY_5_6_3
        tex_importer.textureType = TextureImporterType.Default;
#else
        tex_importer.textureType = TextureImporterType.Advanced;
#endif
        tex_importer.mipmapEnabled = false;
        tex_importer.isReadable = false;

        if(alphaIsTransparency)
        {
            tex_importer.sRGBTexture = true;
            tex_importer.alphaSource = TextureImporterAlphaSource.FromInput;
            tex_importer.alphaIsTransparency = true;
        }
        tex_importer.filterMode = FilterMode.Bilinear;
        tex_importer.spriteImportMode = SpriteImportMode.None;
#if UNITY_5_6_3
        var iphone_set_info = new TextureImporterPlatformSettings();
        iphone_set_info.name = "iPhone";
        iphone_set_info.overridden = true;
        iphone_set_info.maxTextureSize = tex_importer.maxTextureSize;
        iphone_set_info.format = format;
        iphone_set_info.allowsAlphaSplitting = format != TextureImporterFormat.ETC_RGB4;
        tex_importer.SetPlatformTextureSettings(iphone_set_info);

        var android_set_info = new TextureImporterPlatformSettings();
        android_set_info.name = "Android";
        android_set_info.overridden = true;
        android_set_info.maxTextureSize = tex_importer.maxTextureSize;
        android_set_info.format = format;
        iphone_set_info.allowsAlphaSplitting = format != TextureImporterFormat.ETC_RGB4;
        tex_importer.SetPlatformTextureSettings(android_set_info);
#else
        tex_importer.SetPlatformTextureSettings("iPhone", tex_importer.maxTextureSize, format);
        tex_importer.SetPlatformTextureSettings("Android", tex_importer.maxTextureSize, format);
#endif
        tex_importer.SaveAndReimport();
    }

    /// <summary>
    /// 获取图集组件
    /// 如果没有图集组件则自动生成一个，并且会生成相关联的Met
    /// 所有自动生成的组件都会放置会自动生成为预设并且存放在tex_path相同目录之中
    /// </summary>
    private static void SetUIAtlas(string img_asset_path)
    {
        // 图集肯定是png格式的
        string prefab_path = img_asset_path.Replace(".png", ".prefab");
        string json_path = img_asset_path.Replace(".png", ".txt");

        var img_full_path = PathCombine(project_root_path, img_asset_path);
        var txt_full_path = PathCombine(project_root_path, json_path);

        // 如果没有图片就不做处理
        if (!File.Exists(img_full_path) || !File.Exists(txt_full_path))
        {
            Debug.Log("处理图片失败： 图片路径 " + img_full_path + "   找不到该图片或txt存在");
            return;
        }

        //正常条件下会遇到两种情况
        //一是没有图集物体，也没有材质球 这个时候就需要去创建材质球和物体并将物体设置成为预设
        //再则是已经有物体了，就单纯的替换图集
        //再增加一项非正式操作手段的处理办法，直接将所有的图片都删除并且放置新的图片到Unity文件中
        var _go = AssetDatabase.LoadAssetAtPath(prefab_path, typeof(GameObject)) as GameObject;
        var go = PrefabUtility.InstantiatePrefab(_go) as GameObject;
        if (go != null)
        {
            var atlas = go.GetComponent<UIAtlas>();
            var mat = atlas.spriteMaterial;
            if (mat == null)
            {
                mat = AssetDatabase.LoadAssetAtPath<Material>(prefab_path.Replace(".prefab", ".mat"));
                if (mat == null)
                {
                    Debug.LogWarning("Mat can't find assetPath: " + prefab_path);
                    return;
                }
                atlas.spriteMaterial = mat;
            }
            var tex = AssetDatabase.LoadAssetAtPath<Texture2D>(prefab_path.Replace(".prefab", ".png"));
            mat.mainTexture = tex;
            var ta = GetTextAsset(json_path);
            NGUIJson.LoadSpriteData(atlas, ta);
            PrefabUtility.ReplacePrefab(go, _go, ReplacePrefabOptions.ConnectToPrefab);
            UnityEngine.GameObject.DestroyImmediate(go);
            AssetDatabase.SaveAssets();
            Debug.Log("完成设置 设置路径：" + prefab_path);
        }
        else if (!System.IO.File.Exists(project_root_path + prefab_path.Replace("Assets", "")))
        {
            CreateUIAtlas(prefab_path, json_path);
        }
        else
        {
            Debug.Log("The prefab already exists! but can't find prefab asset, waite next import prefab asset!");
        }
    }
    private static TextAsset GetTextAsset(string json_path)
    {
        TextAsset ta = AssetDatabase.LoadAssetAtPath(json_path, typeof(TextAsset)) as TextAsset;
        return ta;
    }

    /// <summary>
    /// 生成图集
    /// 图集里面包含的组件有：Prefab,Met,atlas,shader
    /// </summary>
    private static void CreateUIAtlas(string prefab_path, string json_path)
    {
        // 创建mat和shader
        Shader shader = Shader.Find("Unlit/Transparent Colored");
        Material mat = new Material(shader);
        Texture2D tex = AssetDatabase.LoadAssetAtPath<Texture2D>(prefab_path.Replace(".prefab", ".png"));
        mat.mainTexture = tex;

        // 创建空物体并且添加图集组件 
        // 没有办法直接生成预设，必须生成物体再转换成预设
        string atlas_name = System.IO.Path.GetFileNameWithoutExtension(prefab_path);
        atlas_name = System.IO.Path.GetFileNameWithoutExtension(prefab_path);
        var go = new GameObject(atlas_name);
        var atlas = go.AddComponent(typeof(UIAtlas)) as UIAtlas;
        atlas.spriteMaterial = mat;

        var ta = GetTextAsset(json_path);
        NGUIJson.LoadSpriteData(atlas, ta);

        // 将mat,GameObj做成预设  
        var mat_path = prefab_path.Replace(".prefab", ".mat");
        AssetDatabase.CreateAsset(mat, mat_path);
        var prefab = PrefabUtility.CreateEmptyPrefab(prefab_path);
        PrefabUtility.ReplacePrefab(go, prefab);

        UnityEngine.Object.DestroyImmediate(go);
        // 存储资源
        AssetDatabase.SaveAssets();
    }

#endregion

    public static string PathCombine(string dir_path,string file_path)
    {
        return System.IO.Path.Combine(dir_path, file_path).Replace("\\", "/");
    }
}

static class TextureFormatAssetContainer
{
    private static List<string> m_import_assets = new List<string>();
    private static List<string> m_delect_assets = new List<string>();
    private static Dictionary<string, string> m_moved_assets = new Dictionary<string, string>();

    internal static List<string> ImportAssets
    {
        get { return m_import_assets; }
        set
        {
            var add_list = value;
            if (add_list != null && add_list.Count > 0)
            {
                foreach (var add in add_list)
                {
                    if (!m_import_assets.Contains(add)) m_import_assets.Add(add);
                }
            }
        }
    }

    internal static List<string> DelectAssets
    {
        get { return m_delect_assets; }
        set
        {
            var del_list = value;
            if (del_list != null && del_list.Count > 0)
            {
                foreach (var del in del_list)
                {
                    if (!m_delect_assets.Contains(del)) m_delect_assets.Add(del);
                }
            }
        }
    }

    internal static Dictionary<string, string> MovedAssets
    {
        get { return m_moved_assets; }
        set
        {
            var add_dict = value;
            if (add_dict != null && add_dict.Count > 0)
            {
                foreach (var add in add_dict)
                {
                    if (!m_moved_assets.ContainsKey(add.Key)) m_moved_assets.Add(add.Key, add.Value);
                }
            }
        }
    }

    public static void Clear()
    {
        m_import_assets.Clear();
        m_delect_assets.Clear();
        m_moved_assets.Clear();
    }
}

/// <summary>Texture 的MD5管理类</summary>
class TextureMD5Manager
{
    private static TextureMD5Manager m_instance = null;
    private readonly string log_path = "";
    private Dictionary<string, string> md5_dict = new Dictionary<string, string>();

    internal static TextureMD5Manager Instance
    {
        get
        {
            if (m_instance == null) m_instance = new TextureMD5Manager();
            return m_instance;
        }
    }
    private TextureMD5Manager()
    {
        //log_path = Application.dataPath.Replace("Assets", "PrecessTextureLog/ImageLog.txt");
        TextureFormatAssetContainer.Clear();
        //LoadLog();
    }
    
    /// <summary>
    ///  加载Texture的MD5数据文件
    /// </summary>
    private void LoadLog()
    {
        if(File.Exists(log_path))
        {
            var contents = File.ReadAllLines(log_path,Encoding.UTF8);
            foreach(var content in contents)
            {
                // 存储的时候是按照 full_file_path|MD5 这种格式去存储的
                var str = content.Split('|');
                md5_dict.Add(str[0], str[1]);
            }
        }
    }

    /// <summary>
    /// 存储Texture的MD5数据文件
    /// MD5 查看是否改动过该texture 或者 图集的txt
    /// </summary>
    public void SaveLog()
    {
        StringBuilder str = new StringBuilder();
        foreach(var item in md5_dict)
        {
            str.Append(item.Key);
            str.Append("|");
            str.Append(item.Value);
            str.Append("\n");
        }

        //var dir_path = System.IO.Path.GetDirectoryName(log_path);
        //if (!Directory.Exists(dir_path)) Directory.CreateDirectory(dir_path);
        //File.WriteAllText(log_path, str.ToString(),Encoding.UTF8);
        Debug.Log("存储成功  " + log_path  + "  \t content: \n" + str.ToString());
    }

    public void AddFileMD5(string assets_file_path,string file_md5)
    {
        md5_dict.Add(assets_file_path, file_md5);
    }
    public void ChangeFileMD5(string assets_file_path,string change_to_md5)
    {
        if(md5_dict.ContainsKey(assets_file_path))
        {
            md5_dict[assets_file_path] = change_to_md5;
        }
    }

    public void RemoveFileMD5(string assets_file_path)
    {
        if(md5_dict.ContainsKey(assets_file_path))
        {
            md5_dict.Remove(assets_file_path);
        }
    }

    public bool CheckFileChanged(string assets_file_path,string md5)
    {
        if(md5_dict.ContainsKey(assets_file_path))
        {
            return !md5_dict[assets_file_path].Equals(md5);
        }
        return true;
    }

    public bool CheckHasFileMD5(string assets_file_path)
    {
        return md5_dict.ContainsKey(assets_file_path);
    }

    /// <summary>
    /// 计算文件的MD5校验
    /// </summary>
    public static string GetMD5HashFromFile(string full_file_path)
    {
        try
        {
            FileStream file = new FileStream(full_file_path, FileMode.Open);
            System.Security.Cryptography.MD5 md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
            byte[] retVal = md5.ComputeHash(file);
            file.Close();

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < retVal.Length; i++)
            {
                sb.Append(retVal[i].ToString("x2"));
            }
            return sb.ToString();
        }
        catch (Exception ex)
        {
            throw new Exception("GetMD5HashFromFile() fail,error:" + ex.Message);
        }
    }

    internal void MoveFileMD5(string from_assets_path, string to_assets_path)
    {
        if(md5_dict.ContainsKey(from_assets_path))
        {
            var md5 = md5_dict[from_assets_path];
            md5_dict.Remove(from_assets_path);
            md5_dict.Add(to_assets_path, md5);
        }
    }
}
